﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

using SimpleIR;
using System.Diagnostics.Contracts;

namespace PInfoModel
{
	/// <summary>
	/// Represents reference from an unknown external location.
	/// </summary>
	public sealed class PIExternalRef : IEquatable<PIExternalRef>, IComparable<PIExternalRef>
	{
		/// <summary>
		/// The unique id associated with the external name ref.
		/// </summary>
		public readonly int IDTAG;

		/// <summary>
		/// If this external ref is from a single static location then this is the name of that location.
		/// </summary>
		internal readonly IRStaticField StaticRef;

		public PIExternalRef(int idtag, IRStaticField staticloc)
		{
			this.IDTAG = idtag;
			this.StaticRef = staticloc;
		}

		public override string ToString()
		{
 			return "$ext_" + (this.StaticRef != null ? (this.StaticRef.ToString() + "_") : "") + this.IDTAG; 
		}

		internal void Serialize(PInfoSerializer sr)
		{
			sr.WriteStartTag("ExtRef");

			sr.WriteInt("ID", this.IDTAG);
			sr.WriteBool("HasStatic", this.StaticRef != null);

			if(this.StaticRef != null)
				sr.WriteIRRoot("OptRoot", this.StaticRef);
			
			sr.WriteEndTag("ExtRef");
		}

		internal static PIExternalRef Deserialize(PInfoDeSerializer dsr)
		{
			dsr.ReadStartTag("ExtRef");
			int id = dsr.ReadInt("ID");
			bool hasstatic = dsr.ReadBool("HasStatic");

			IRStaticField sf = null;
			if(hasstatic)
				sf = (IRStaticField)dsr.ReadIRRoot("OptRoot");

			dsr.ReadEndTag("ExtRef");

			return new PIExternalRef(id, sf);
		}

		public override int GetHashCode()
		{ return this.IDTAG; }

		public override bool Equals(object obj)
		{ return (obj is PIExternalRef) && this.Equals((PIExternalRef)obj); }

		public bool Equals(PIExternalRef other)
		{ return this.IDTAG == other.IDTAG; }

		public int CompareTo(PIExternalRef other)
		{ return this.IDTAG - other.IDTAG; }
	}

	/// <summary>
	/// Support for memory location use and mod.
	/// </summary>
	public struct PIUseMod : IEquatable<PIUseMod>, IComparable<PIUseMod>
	{
		public enum Value : byte
		{
			UseAsInternal = 0x1,
			ModAsInternal = 0x2,
			UseAsExternal = 0x4,
			ModAsExternal = 0x8
		};

		/// <summary>
		/// Tag indicating if this is a use or a modification.
		/// </summary>
		public readonly Value Tag;

		/// <summary>
		/// The line number of the use/mod.
		/// </summary>
		public readonly int Line;

		/// <summary>
		/// The location (as an access path) that is used or modified.
		/// </summary>
		public readonly IRBasicAccessPath UMPath;

		public PIUseMod(Value tag, int line, IRBasicAccessPath pth)
		{
			this.Tag = tag;
			this.Line = line;
			this.UMPath = pth;
		}

		public override string ToString()
		{
			string tags;
			if(this.Tag == Value.UseAsInternal)
				tags = "UI";
			else if(this.Tag == Value.UseAsExternal)
				tags = "UE";
			else if(this.Tag == Value.ModAsInternal)
				tags = "MI";
			else
				tags = "ME";

			string ltt = this.UMPath.ToString();

			return "[" + tags + "@" + this.Line.ToString() + " on " + ltt + "]";
		}

		internal void Serialize(PInfoSerializer sr)
		{
			sr.WriteStartTag("UMInfo");
			sr.WriteInt("Tag", (int)this.Tag);
			sr.WriteInt("Line", this.Line);
			sr.WriteIRBasicAccessPath("UMP", this.UMPath);
			sr.WriteEndTag("UMInfo");
		}

		internal static PIUseMod Deserialize(PInfoDeSerializer dsr)
		{
			dsr.ReadStartTag("UMInfo");
			Value tg = (Value)dsr.ReadInt("Tag");
			int line = dsr.ReadInt("Line");
			IRBasicAccessPath ump = dsr.ReadIRBasicAccessPath("UMP");
			dsr.ReadEndTag("UMInfo");

			return new PIUseMod(tg, line, ump);
		}

		/// <summary>
		/// Return true if this location has had any value used in the given range.
		/// </summary>
		public static bool HasLocationBeenUsedInRange_Any(PIUseMod[] uml, int min, int max)
		{
			for(int i = 0; i < uml.Length; ++i)
			{
				bool isuse = (uml[i].Tag == Value.UseAsExternal) | (uml[i].Tag == Value.UseAsInternal);
				bool isinrgn = (min <= uml[i].Line) & (uml[i].Line <= max);
				
				if(isuse & isinrgn)
					return true;
			}

			return false;
		}

		/// <summary>
		/// Return true if this location has had a pointer value used in the given range.
		/// </summary>
		public static bool HasLocationBeenUsedInRange_PtrRef(PIUseMod[] uml, IRType loctype, int min, int max)
		{
			for(int i = 0; i < uml.Length; ++i)
			{
				bool isuse = (uml[i].Tag == Value.UseAsExternal) | (uml[i].Tag == Value.UseAsInternal);
				bool isptr;
				if(uml[i].UMPath.IsEmptyAccessPath)
					isptr = (loctype is IRPtrType) | (loctype is IRReferenceType);
				else
					isptr = uml[i].UMPath.PathLast.FieldType is IRPtrType;

				bool isinrgn = (min <= uml[i].Line) & (uml[i].Line <= max);
				
				if(isuse & isptr & isinrgn)
					return true;
			}

			return false;
		}

		/// <summary>
		/// Return true if this location has had a scalar value used in the given range.
		/// </summary>
		public static bool HasLocationBeenUsedInRange_Scalar(PIUseMod[] uml, IRType loctype, int min, int max)
		{
			for(int i = 0; i < uml.Length; ++i)
			{
				bool isuse = (uml[i].Tag == Value.UseAsExternal) | (uml[i].Tag == Value.UseAsInternal);
				bool isscalar;
				if(uml[i].UMPath.IsEmptyAccessPath)
					isscalar = (loctype is IRNumericType) | (loctype is IREnumType);
				else
					isscalar = (uml[i].UMPath.PathLast.FieldType is IRNumericType) | (uml[i].UMPath.PathLast.FieldType is IRNumericType);

				bool isinrgn = (min <= uml[i].Line) & (uml[i].Line <= max);
		
				if(isuse & isscalar & isinrgn)
					return true;
			}

			return false;
		}

		/// <summary>
		/// Return true if this location has had any value modified in the given range.
		/// </summary>
		public static bool HasLocationBeenModifiedInRange_Any(PIUseMod[] uml, int min, int max)
		{
			for(int i = 0; i < uml.Length; ++i)
			{
				bool ismod = (uml[i].Tag == Value.ModAsExternal) | (uml[i].Tag == Value.ModAsInternal);
				bool isinrgn = (min <= uml[i].Line) & (uml[i].Line <= max);
				
				if(ismod & isinrgn)
					return true;
			}

			return false;
		}

		/// <summary>
		/// Return true if this location has had a pointer value modified in the given range.
		/// </summary>
		public static bool HasLocationBeenModifiedInRange_PtrRef(PIUseMod[] uml, IRType loctype, int min, int max)
		{
			for(int i = 0; i < uml.Length; ++i)
			{
				bool ismod = (uml[i].Tag == Value.ModAsExternal) | (uml[i].Tag == Value.ModAsInternal);
				bool isptr;
				if(uml[i].UMPath.IsEmptyAccessPath)
					isptr = (loctype is IRPtrType) | (loctype is IRReferenceType);
				else
					isptr = uml[i].UMPath.PathLast.FieldType is IRPtrType;

				bool isinrgn = (min <= uml[i].Line) & (uml[i].Line <= max);
		
				if(ismod & isptr & isinrgn)
					return true;
			}

			return false;
		}

		/// <summary>
		/// Return true if this location has had a scalar value modified in the given range.
		/// </summary>
		public static bool HasLocationBeenModifiedInRange_Scalar(PIUseMod[] uml, IRType loctype, int min, int max)
		{
			for(int i = 0; i < uml.Length; ++i)
			{
				bool ismod = (uml[i].Tag == Value.ModAsExternal) | (uml[i].Tag == Value.ModAsInternal);
				bool isscalar;
				if(uml[i].UMPath.IsEmptyAccessPath)
					isscalar = (loctype is IRNumericType) | (loctype is IREnumType);
				else
					isscalar = (uml[i].UMPath.PathLast.FieldType is IRNumericType) | (uml[i].UMPath.PathLast.FieldType is IRNumericType);

				bool isinrgn = (min <= uml[i].Line) & (uml[i].Line <= max);
				
				if(ismod & isscalar & isinrgn)
					return true;
			}

			return false;
		}

		public static bool operator ==(PIUseMod um1, PIUseMod um2)
		{ return um1.Equals(um2); }

		public static bool operator !=(PIUseMod um1, PIUseMod um2)
		{ return !um1.Equals(um2); }

		public static bool operator <(PIUseMod um1, PIUseMod um2)
		{ return um1.CompareTo(um2) < 0; }

		public static bool operator >(PIUseMod um1, PIUseMod um2)
		{ return um1.CompareTo(um2) > 0; }

		public override int GetHashCode()
		{ return (((int)this.Tag) ^ this.Line ^ this.UMPath.GetHashCode()); }

		public override bool Equals(Object obj)
		{ return (obj is PIUseMod) && this.Equals((PIUseMod)obj); }

		public bool Equals(PIUseMod other)
		{ return (this.Tag == other.Tag) & (this.Line == other.Line) & (this.UMPath == other.UMPath); }

		public int CompareTo(PIUseMod other)
		{
			int ofcv = this.UMPath.CompareTo(other.UMPath);
			if(ofcv != 0)
				return ofcv;
			else
			{
				int linev = (this.Line - other.Line);
				return (linev != 0) ? linev : ((int)this.Tag) - ((int)other.Tag);
			}
		}
	}

	/// <summary>
	/// This struct tracks the info for a single entry of an allocated type/location/identity that reaches this region.
	/// </summary>
	public struct PIEscapeSite : IEquatable<PIEscapeSite>, IComparable<PIEscapeSite>
	{
		/// <summary>
		/// This is the most recent escape, or alloction site associated with this object.
		/// </summary>
		public readonly int EscLine;

		/// <summary>
		/// This tracks the flow time of the alloc/escape in terms of loop current/previous iterations -- initially set to the line
		/// </summary>
		public readonly int EscFlowLoop;

		/// <summary>
		/// This is a hash of the escape path for this allocation.
		/// </summary>
		public readonly int EscIdentity;

		/// <summary>
		/// Create a <c>EscapeSiteInfo</c> with the given values.
		/// </summary>
		public PIEscapeSite(int escline, int escloop, int escid)
		{
			this.EscLine = escline;
			this.EscFlowLoop = escloop;
			this.EscIdentity = escid;
		}

		public override string ToString()
		{
			return "el" + this.EscLine.ToString() + ":ll" + this.EscFlowLoop.ToString() + "@i" + this.EscIdentity.ToString();
		}

		internal void Serialize(PInfoSerializer sr)
		{
			sr.WriteStartTag("Esc");
			sr.WriteInt("Line", this.EscLine);
			sr.WriteInt("Flow", this.EscFlowLoop);
			sr.WriteInt("ID", this.EscIdentity);
			sr.WriteEndTag("Esc");
		}

		internal static PIEscapeSite Deserialize(PInfoDeSerializer dsr)
		{
			dsr.ReadStartTag("Esc");
			int line = dsr.ReadInt("Line");
			int flow = dsr.ReadInt("Flow");
			int id = dsr.ReadInt("ID");
			dsr.ReadEndTag("Esc");

			return new PIEscapeSite(line, flow, id);
		}

		public static bool operator ==(PIEscapeSite esc1, PIEscapeSite esc2)
		{ return esc1.Equals(esc2); }

		public static bool operator !=(PIEscapeSite esc1, PIEscapeSite esc2)
		{ return !esc1.Equals(esc2); }

		public static bool operator <(PIEscapeSite esc1, PIEscapeSite esc2)
		{ return esc1.CompareTo(esc2) < 0; }

		public static bool operator >(PIEscapeSite esc1, PIEscapeSite esc2)
		{ return esc1.CompareTo(esc2) > 0; }

		public override int GetHashCode()
		{ return (this.EscLine ^ this.EscFlowLoop ^ this.EscIdentity); }

		public override bool Equals(Object obj)
		{ return (obj is PIEscapeSite) && this.Equals((PIEscapeSite)obj); }

		public bool Equals(PIEscapeSite other)
		{ return (this.EscLine == other.EscLine) & (this.EscFlowLoop == other.EscFlowLoop) & (this.EscIdentity == other.EscIdentity); }

		public int CompareTo(PIEscapeSite other)
		{
			int valesci = this.EscIdentity - other.EscIdentity;
			if(valesci != 0)
				return valesci;
			else
			{
				int valesc = this.EscLine - other.EscLine;
				return (valesc != 0) ? valesc : this.EscFlowLoop - other.EscFlowLoop;
			}
		}
	}
}
